package org.example;

import com.alibaba.fastjson.JSONObject;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Point;
import org.geotools.data.FileDataStore;
import org.geotools.data.FileDataStoreFinder;
import org.geotools.data.shapefile.ShapefileDataStore;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.geojson.feature.FeatureJSON;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.json.simple.JSONArray;
import org.opengis.feature.Property;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.*;

/**
 * @author lzh
 * @date 2023/9/9 10:34
 * @description .
 */
public class ParsingShpFileUtils {

    public static void main(String[] args) throws Exception {
        System.out.println(ParsingShpFile("/usr/local/xxx.shp"));
    }

    public static double[] GaussToBL(double X, double Y) {
        double[] output = new double[2];
        double longitude1, latitude1, longitude0, X0, Y0, xval, yval;
        //NN曲率半径，测量学里面用N表示
        //M为子午线弧长，测量学里用大X表示
        //fai为底点纬度，由子午弧长反算公式得到，测量学里用Bf表示
        //R为底点所对的曲率半径，测量学里用Nf表示
        double e1, e2, f, a, ee, NN, T, C, M, D, R, u, fai, iPI;
        iPI = 0.0174532925199433;
        a = 6378137.0;
        f = 1 / 298.257222101; //CGCS2000坐标系参数
        //a=6378137.0; f=1/298.2572236; //wgs84坐标系参数
        longitude0 = 120.0;//中央子午线
        longitude0 = longitude0 * iPI; //中央经线

        X0 = 500000L;
        Y0 = 0;
        xval = X - X0;
        yval = Y - Y0; //带内大地坐标
        e2 = 2 * f - f * f;
        e1 = (1.0 - Math.sqrt(1 - e2)) / (1.0 + Math.sqrt(1 - e2));
        ee = e2 / (1 - e2);
        M = yval;
        u = M / (a * (1 - e2 / 4 - 3 * e2 * e2 / 64 - 5 * e2 * e2 * e2 / 256));
        fai = u + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * Math.sin(2 * u) + (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * Math.sin(4 * u) + (151 * e1 * e1 * e1 / 96) * Math.sin(6 * u) + (1097 * e1 * e1 * e1 * e1 / 512) * Math.sin(8 * u);
        C = ee * Math.cos(fai) * Math.cos(fai);
        T = Math.tan(fai) * Math.tan(fai);
        NN = a / Math.sqrt(1.0 - e2 * Math.sin(fai) * Math.sin(fai));
        R = a * (1 - e2) / Math.sqrt((1 - e2 * Math.sin(fai) * Math.sin(fai)) * (1 - e2 * Math.sin(fai) * Math.sin(fai)) * (1 - e2 * Math.sin(fai) * Math.sin(fai)));
        D = xval / NN;
        //计算经度(Longitude) 纬度(Latitude)
        longitude1 = longitude0 + (D - (1 + 2 * T + C) * D * D * D / 6 + (5 - 2 * C + 28 * T - 3 * C * C + 8 * ee + 24 * T * T) * D * D * D * D * D / 120) / Math.cos(fai);
        latitude1 = fai - (NN * Math.tan(fai) / R) * (D * D / 2 - (5 + 3 * T + 10 * C - 4 * C * C - 9 * ee) * D * D * D * D / 24 + (61 + 90 * T + 298 * C + 45 * T * T - 256 * ee - 3 * C * C) * D * D * D * D * D * D / 720);
        //转换为度 DD
        output[0] = longitude1 / iPI;
        output[1] = latitude1 / iPI;
        return output;
    }

    /**
     * 解析shp文件
     *
     * @param filePath
     * @return
     * @throws Exception
     */
    public static Map ParsingShpFile(String filePath) throws Exception {
        File file = new File(filePath);
        if (!file.exists()) {
            throw new Exception("文件不存在!");
        }
        if (!filePath.endsWith("shp")) {
            throw new Exception("只能指定后缀为shp的文件");
        }
        Map map = new HashMap();
        List<Map> list = new ArrayList();
        //读取shp
        SimpleFeatureCollection colls1 = readShp(filePath);
        SimpleFeatureType schema = colls1.getSchema();
        Name name = schema.getGeometryDescriptor().getType().getName();
        ReferencedEnvelope bounds = colls1.getBounds();
        //拿到所有features
        SimpleFeatureIterator iters = colls1.features();
        String s = name.toString();
        if ("Point".equals(s)) {
            list = parsingPoint(iters);
        } else if ("MultiLineString".equals(s) || "MultiPolygon".equals(s)) {
            list = parsingLineOrPoly(iters);
        }
        map.put("data", list);
        map.put("maxX", bounds.getMaxX());
        map.put("minX", bounds.getMinX());
        map.put("maxY", bounds.getMaxY());
        map.put("minY", bounds.getMinY());
        map.put("shapeFile", name.toString());
        return map;
    }

    /**
     * 解析点数据
     *
     * @param iters
     * @return
     */
    public static List<Map> parsingPoint(SimpleFeatureIterator iters) {
        List<Map> list = new ArrayList();
        while (iters.hasNext()) {
            SimpleFeature sf = iters.next();
            Map map = new HashMap();
            Iterator<? extends Property> iterator = sf.getValue().iterator();
            while (iterator.hasNext()) {
                Property property = iterator.next();
                if (property.getValue() instanceof Point) {
                    map.put("PointX", ((Point) (property.getValue())).getX());
                    map.put("PointY", ((Point) (property.getValue())).getY());
                } else {
                    Name name = property.getName();//属性名称
                    Object value = property.getValue();//属性值
                    map.put(name, value);
                }
            }
            list.add(map);
        }
        iters.close();
        return list;
    }

    /**
     * 解析线和面
     *
     * @param iters
     * @return
     */
    public static List<Map> parsingLineOrPoly(SimpleFeatureIterator iters) {
        List<Map> list = new ArrayList();
        while (iters.hasNext()) {
            SimpleFeature sf = iters.next();
            Map map = new HashMap();
            Iterator<? extends Property> iterator = sf.getValue().iterator();
            while (iterator.hasNext()) {
                Property property = iterator.next();
                if (property.getValue() instanceof Geometry) {
                    Geometry geometry = (Geometry) property.getValue();
                    Coordinate[] coordinates = geometry.getCoordinates();
                    List<Map> paths = new ArrayList<Map>();
                    for (Coordinate coordinate : coordinates) {
                        Map path = new HashMap();
                        path.put("x", coordinate.x);
                        path.put("y", coordinate.y);
                        path.put("z", coordinate.z);
                        paths.add(path);
                    }
                    map.put("path", paths);
                } else {
                    Name name = property.getName();//属性名称
                    Object value = property.getValue();//属性值
                    map.put(name, value);
                }
            }
            list.add(map);
        }
        iters.close();
        return list;
    }

    public static SimpleFeatureCollection readShp(String path) {
        return readShp(path, null);

    }

    public static SimpleFeatureCollection readShp(String path, Filter filter) {
        SimpleFeatureSource featureSource = readStoreByShp(path);
        if (featureSource == null) {
            return null;
        }
        try {
            return filter != null ? featureSource.getFeatures(filter) : featureSource.getFeatures();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static SimpleFeatureSource readStoreByShp(String path) {
        File file = new File(path);
        FileDataStore store;
        SimpleFeatureSource featureSource = null;
        try {
            store = FileDataStoreFinder.getDataStore(file);
            ((ShapefileDataStore) store).setCharset(Charset.forName("UTF-8"));
            featureSource = store.getFeatureSource();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return featureSource;
    }

    /**
     * shp转换为Geojson
     *
     * @param shpPath  shp文件地址
     * @param jsonPath 要写入的json文件地址
     * @return
     */
    public static String shape2Geojson(String shpPath, String jsonPath) {
        FeatureJSON fjson = new FeatureJSON();
        StringBuffer sb = new StringBuffer();
        try {
            sb.append("{\"type\": \"FeatureCollection\",\"features\": ");

            //读取shp
            SimpleFeatureCollection colls = readShp(shpPath);
            //拿到所有features
            SimpleFeatureIterator itertor = colls.features();
            JSONArray array = new JSONArray();
            while (itertor.hasNext()) {
                SimpleFeature feature = itertor.next();
                StringWriter writer = new StringWriter();
                fjson.writeFeature(feature, writer);
                JSONObject json = JSONObject.parseObject(writer.toString());
                array.add(json);
            }
            itertor.close();
            sb.append(array.toString());
            sb.append("}");

            //写入文件
            FileOutputStream fos = new FileOutputStream(jsonPath, false);
//true表示在文件末尾追加
            fos.write(sb.toString().getBytes());
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();

        }
        return sb.toString();
    }

    public static void getBoundary() {

    }

}
